package com.tarena.tp.luban.worker.server.common.util;

import lombok.extern.slf4j.Slf4j;
import org.apache.http.HttpEntity;
import org.apache.http.HttpMessage;
import org.apache.http.NameValuePair;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.client.utils.URLEncodedUtils;
import org.apache.http.config.SocketConfig;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.DefaultConnectionReuseStrategy;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.DefaultConnectionKeepAliveStrategy;
import org.apache.http.impl.client.DefaultHttpRequestRetryHandler;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.message.BasicHeader;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.TimeUnit;

@Slf4j
public class HttpClient {

    private static final String DEFAULT_CHARSET = "UTF-8";

    private static final String QUESTION_CHAR = "?";

    private static final String AND_CHAR = "&";

    private static final RequestConfig config = RequestConfig.custom()
            // 设置从连接池获取连接的等待超时时间
            .setConnectionRequestTimeout(1000)
            // 设置连接超时间（设置url建立连接超时5秒）
            .setConnectTimeout(5000)
            // 设置等待数据超时间时间（设置获取数据等待超时时间30秒）
            .setSocketTimeout(30000)
            //
            .build();

    private static final SocketConfig socketConfig = SocketConfig.custom().setTcpNoDelay(true).build();

    private static final CloseableHttpClient client = HttpClientBuilder.create()
            //
            .setDefaultSocketConfig(socketConfig)
            // 设置整个连接池的最大连接数
            .setMaxConnTotal(1000)
            // 每个路由最大连接数
            .setMaxConnPerRoute(200)
            //
            .setDefaultRequestConfig(config)
            // 连接池不是共享模式
            .setConnectionManagerShared(false)
            // 连接重用策略，即是否能keepAlive
            .setConnectionReuseStrategy(DefaultConnectionReuseStrategy.INSTANCE)
            // 长连接配置，即获取长连接生产多长时间, 返回值小于等于0时，会无限期保持
            .setKeepAliveStrategy(DefaultConnectionKeepAliveStrategy.INSTANCE)
            //.setSSLHostnameVerifier((a, b) -> true)
            // 定期回收空闲连接
            .evictIdleConnections(300, TimeUnit.SECONDS)
            // 定期回收过期连接
            .evictExpiredConnections()
            // 连接存活时间，如果不设置，则根据长连接信息决定
            .setConnectionTimeToLive(300, TimeUnit.SECONDS)
            // 设置重试次数，默认是3次，当前是禁用掉
            .setRetryHandler(new DefaultHttpRequestRetryHandler(0, false))
            .build();

    static {
        // 程序关闭时关闭连接池
        Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    client.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }));
    }

    /**
     * 设置http headers
     *
     * @param message
     * @param headers
     */
    private static void setHeaders(HttpMessage message, Map<String, String> headers) {
        if (message == null) {
            return;
        }
        if (headers != null && !headers.isEmpty()) {
            for (Entry<String, String> entry : headers.entrySet()) {
                message.addHeader(new BasicHeader(entry.getKey(), entry.getValue()));
            }
        }
        // message.setHeader(HTTP.CONN_KEEP_ALIVE, String.valueOf(TIME_TO_LIVE));
    }

    /**
     * 执行请求
     *
     * @param request
     * @return 返回执行结果
     * @throws IOException
     */
    private static String execute(HttpRequestBase request) throws IOException {
        try (CloseableHttpResponse response = client.execute(request);) {
            HttpEntity he = response.getEntity();
            if (he != null && he.isStreaming()) {
                return EntityUtils.toString(he, DEFAULT_CHARSET);
            }
        } catch (IOException e) {
//            log.error(e.getMessage(), e);
            throw e;
        }
        return null;
    }

    private static List<NameValuePair> toNameValuePairList(Map<String, String> parameters) {
        List<NameValuePair> pairs = null;
        if (null != parameters) {
            pairs = new ArrayList<>(parameters.size());
            for (Entry<String, String> entry : parameters.entrySet()) {
                NameValuePair p = new BasicNameValuePair(entry.getKey(), entry.getValue());
                pairs.add(p);
            }
        }
        return pairs;
    }

    public static String post(String url, String msg) throws IOException {
        return post(url, msg, null);
    }

    /**
     * POST 请求
     *
     * @param url
     * @param msg
     * @param headers
     * @return
     * @throws IOException
     */
    public static String post(String url, String msg, Map<String, String> headers) throws IOException {
        HttpPost request = new HttpPost(url);
        setHeaders(request, headers);
        HttpEntity entity = new StringEntity(msg, DEFAULT_CHARSET);
        request.setEntity(entity);
        return execute(request);
    }

    public static String post(String url, Map<String, String> parameters) throws IOException {
        return post(url, parameters, null);
    }

    /**
     * POST 请求
     *
     * @param url
     * @param parameters 表单参数
     * @param headers
     * @return
     * @throws IOException
     */
    public static String post(String url, Map<String, String> parameters, Map<String, String> headers)
            throws IOException {
        HttpPost request = new HttpPost(url);
        setHeaders(request, headers);
        List<NameValuePair> pairs = toNameValuePairList(parameters);
        if (null != pairs && !pairs.isEmpty()) {
            UrlEncodedFormEntity entity = new UrlEncodedFormEntity(pairs, DEFAULT_CHARSET);
            request.setEntity(entity);
        }

        return execute(request);
    }

    public static String get(String url)
            throws IOException {
        return get(url, null, null);
    }

    /**
     * GET 请求
     *
     * @param url
     * @param parameters
     * @param headers
     * @return
     * @throws IOException
     */
    public static String get(String url, Map<String, String> parameters, Map<String, String> headers)
            throws IOException {
        List<NameValuePair> pairs = toNameValuePairList(parameters);
        String params = null;
        if (null != pairs && !pairs.isEmpty()) {
            params = URLEncodedUtils.format(pairs, DEFAULT_CHARSET);
        }
        StringBuilder urlBuilder = new StringBuilder(url);
        if (null != params && !params.isEmpty()) {
            if (url.contains(QUESTION_CHAR)) {
                urlBuilder.append(AND_CHAR);
            } else {
                urlBuilder.append(QUESTION_CHAR);
            }
            urlBuilder.append(params);
        }

        HttpGet request = new HttpGet(urlBuilder.toString());
        setHeaders(request, headers);
        return execute(request);
    }
}
